// Package repo provides the data access layer for the application.
package repo

import (
	"strings"

	"github.com/sysadminsmedia/homebox/backend/internal/core/services/reporting/eventbus"
	"github.com/sysadminsmedia/homebox/backend/internal/data/ent"
	"github.com/sysadminsmedia/homebox/backend/internal/sys/config"
)

// AllRepos is a container for all the repository interfaces
type AllRepos struct {
	Users       *UserRepository
	AuthTokens  *TokenRepository
	Groups      *GroupRepository
	Locations   *LocationRepository
	Labels      *LabelRepository
	Items       *ItemsRepository
	Attachments *AttachmentRepo
	MaintEntry  *MaintenanceEntryRepository
	Notifiers   *NotifierRepository
	
	// Store references for hot reload
	db        *ent.Client
	bus       *eventbus.EventBus
	storage   config.Storage
	pubSubConn string
	thumbnail config.Thumbnail
}

func New(db *ent.Client, bus *eventbus.EventBus, storage config.Storage, pubSubConn string, thumbnail config.Thumbnail) *AllRepos {
	repos := &AllRepos{
		Users:       &UserRepository{db},
		AuthTokens:  &TokenRepository{db},
		Groups:      NewGroupRepository(db),
		Locations:   &LocationRepository{db, bus},
		Labels:      &LabelRepository{db, bus},
		Items:       &ItemsRepository{db, bus},
		Attachments: &AttachmentRepo{db, storage, pubSubConn, thumbnail},
		MaintEntry:  &MaintenanceEntryRepository{db},
		Notifiers:   NewNotifierRepository(db),
		
		// Store for hot reload
		db:        db,
		bus:       bus,
		storage:   storage,
		pubSubConn: pubSubConn,
		thumbnail: thumbnail,
	}
	return repos
}

// Close closes all database connections
func (r *AllRepos) Close() error {
	if r.db != nil {
		// Step 1: Execute SQLite commands to ensure clean shutdown
		db := r.db.Sql()
		if db != nil {
			// Close any open transactions
			db.Exec("PRAGMA optimize")
			// Force WAL checkpoint to merge WAL file back into main database
			db.Exec("PRAGMA wal_checkpoint(FULL)")
			// Switch to DELETE mode to close WAL files
			db.Exec("PRAGMA journal_mode=DELETE")
			// Final checkpoint
			db.Exec("PRAGMA wal_checkpoint(TRUNCATE)")
		}
		
		// Step 2: Close the Ent client
		if err := r.db.Close(); err != nil {
			return err
		}
		
		// Step 3: Set to nil to prevent reuse
		r.db = nil
	}
	return nil
}

// DB returns the underlying database client
func (r *AllRepos) DB() *ent.Client {
	return r.db
}

// Reconnect reconnects to the database with new configuration
func (r *AllRepos) Reconnect(cfg *config.Config) error {
	// Close existing connection
	if r.db != nil {
		r.db.Close()
	}
	
	// Create new database connection
	databaseURL := cfg.Database.SqlitePath
	if databaseURL == "" {
		databaseURL = "./data/homebox.db"
	}
	
	// Remove query parameters for the file path, but keep them for the connection
	connectionURL := databaseURL
	if !strings.Contains(databaseURL, "?") {
		// Add default SQLite parameters if not present
		connectionURL = databaseURL + "?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1&_time_format=sqlite"
	}
	
	newClient, err := ent.Open("sqlite3", connectionURL)
	if err != nil {
		return err
	}
	
	// Update client reference
	r.db = newClient
	
	// Reinitialize all repositories with new client
	r.Users = &UserRepository{newClient}
	r.AuthTokens = &TokenRepository{newClient}
	r.Groups = NewGroupRepository(newClient)
	r.Locations = &LocationRepository{newClient, r.bus}
	r.Labels = &LabelRepository{newClient, r.bus}
	r.Items = &ItemsRepository{newClient, r.bus}
	r.Attachments = &AttachmentRepo{newClient, r.storage, r.pubSubConn, r.thumbnail}
	r.MaintEntry = &MaintenanceEntryRepository{newClient}
	r.Notifiers = NewNotifierRepository(newClient)
	
	return nil
}
